package models

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"github.com/XieWeikai/PlaceToGo/utils"
	"gorm.io/gorm"
	"time"
)

type RequestStatus string

const (
	Completed          RequestStatus = "accepted"
	WaitingForResponse RequestStatus = "pending"
	RequestCancelled   RequestStatus = "cancelled"
	Expired            RequestStatus = "rejected"
)

type DateTime time.Time

// Value implements the driver Valuer interface, making sure that the DateTime
// can be stored in the database correctly.
func (dt DateTime) Value() (driver.Value, error) {
	return time.Time(dt), nil
}

// Scan implements the sql.Scanner interface, making sure that you can read
// a DateTime from the database correctly.
func (dt *DateTime) Scan(value interface{}) error {
	var t time.Time

	// 根据 value 的实际类型进行处理
	switch v := value.(type) {
	case time.Time:
		t = v
	case []byte:
		// 如果 value 是字节切片，尝试解析它
		var err error
		t, err = time.Parse(time.RFC3339, string(v))
		if err != nil {
			return err
		}
	case string:
		// 如果 value 是字符串，尝试解析它
		var err error
		t, err = time.Parse(time.RFC3339, v)
		if err != nil {
			return err
		}
	case nil:
		// 如果 value 是 nil，我们可能想要将它设置为零值或其它
		// 在本例中，直接返回 nil 错误，因为没有数据可供扫描
		return nil
	default:
		return errors.New("invalid value type for DateTime")
	}

	// 如果前面的转换成功，将转换后的时间赋值给 dt
	*dt = DateTime(t)
	return nil
}

func (t *DateTime) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	tt, err := time.Parse("2006-01-02", s)
	if err != nil {
		return err
	}

	*t = DateTime(tt)
	return nil
}

type Request struct {
	gorm.Model
	UserID         uint          `gorm:"not null"`
	PlaceType      string        `gorm:"type:varchar(50);not null" json:"placeType" binding:"required"`
	Title          string        `gorm:"not null" json:"requestTitle" binding:"required"`
	Description    string        `json:"requestDescription"`
	ImageURL       string        `json:"placeImage" binding:"required"` // 假设图片和文件通过URL提供，存储外部链接
	MaxPrice       float64       `gorm:"not null" json:"maxPrice" binding:"required"`
	RequestEndDate DateTime      `gorm:"not null" json:"endTime" binding:"required"`
	Status         RequestStatus `gorm:"type:varchar(50);not null"`
	Responses      []Response
}

type UpdateRequestDTO struct {
	PlaceType      string   `json:"placeType"`
	Title          string   `json:"requestTitle"`
	Description    string   `json:"requestDescription"`
	ImageURL       string   `json:"placeImage"` // 假设图片和文件通过URL提供，存储外部链接
	MaxPrice       float64  `json:"maxPrice"`
	RequestEndDate DateTime `json:"endTime"`
}

func (r *Request) MarshalJSON() ([]byte, error) {
	return json.Marshal(
		map[string]interface{}{
			"id":                 r.ID,
			"userId":             r.UserID,
			"placeType":          r.PlaceType,
			"requestTitle":       r.Title,
			"requestDescription": r.Description,
			"placeImage":         r.ImageURL,
			"maxPrice":           r.MaxPrice,
			"endTime":            time.Time(r.RequestEndDate),
			"creationTime":       r.CreatedAt,
			"status":             r.Status,
		},
	)
}

func (r *Request) CreateRequest() (*Request, error) {
	r.Status = WaitingForResponse
	if e := DB.Create(r).Error; e != nil {
		return nil, e
	}
	return r, nil
}

func (r *Request) GetFirst(columns ...string) (*Request, error) {
	query := DB.Where(r)
	if len(columns) > 0 {
		query = query.Select(columns)
	}
	e := query.First(r).Error

	if e != nil {
		return nil, e
	}
	return r, nil
}

func (r *Request) GetRequests(limit int, offset int, sortBy string, sortOrder string, columns ...string) ([]Request, error) {
	var requests []Request
	query := DB.Where(r)

	if sortBy != "" {
		// 添加排序，sortOrder 应为 "desc" 或 "asc"
		query = query.Order(sortBy + " " + sortOrder)
	}

	// 添加分页条件
	query = query.Limit(limit).Offset(offset)

	if len(columns) > 0 {
		query = query.Select(columns)
	}

	if err := query.Find(&requests).Error; err != nil {
		return nil, err
	}

	return requests, nil
}

func GetRequests(filter map[string]any, limit int, offset int, sortBy string, sortOrder string, columns ...string) ([]Request, int64, error) {
	var requests []Request
	query := DB.Model(&Request{})

	if q, ok := filter["q"]; ok {
		// 如果存在 q 参数，将其从 filter 中删除
		delete(filter, "q")

		// 将查询字符串转换为 GORM 查询
		qs, args := utils.SearchQuery(q.(string), "title", "description", "place_type", "status")
		//log.Println("qs: ", qs, "args: ", args)
		query = query.Where(qs, args...)
	}

	query = query.Where(filter)

	if sortBy != "" {
		query = query.Order(sortBy + " " + sortOrder)
	}

	var cnt int64
	if err := query.Count(&cnt).Error; err != nil {
		return nil, 0, err
	}

	query = query.Limit(limit).Offset(offset)

	if len(columns) > 0 {
		query = query.Select(columns)
	}

	if err := query.Find(&requests).Error; err != nil {
		return nil, 0, err
	}

	return requests, cnt, nil
}

func (r *Request) UpdateById(u *UpdateRequestDTO) error {
	return DB.Model(r).Updates(u).Error
}

func (r *Request) Cancel() error {
	tx := DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	tx.Model(r).Update("status", RequestCancelled)
	if e := tx.Error; e != nil {
		tx.Rollback()
		return e
	}

	tx.Model(&Response{}).Where("request_id = ? and status = ?", r.ID, Pending).Update("status", Declined)
	if e := tx.Error; e != nil {
		tx.Rollback()
		return e
	}

	return tx.Commit().Error
}

// AfterFind
// hook for check if request is expired
func (r *Request) AfterFind(tx *gorm.DB) (err error) {
	t := time.Time(r.RequestEndDate)
	if t.Before(time.Now()) && r.Status == WaitingForResponse {
		r.Status = Expired
		if e := DB.Save(r).Error; e != nil {
			return e
		}
	}
	return nil
}
